#include "heap.h"
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* ******************************************************************
 *                        PRUEBAS UNITARIAS							*
 * *****************************************************************/
 
/* Función auxiliar para imprimir si estuvo OK o no. */
void print_test (char* name, bool result) {
    printf("%s: %s\n", name, result? "OK" : "ERROR");
}

// Función de comparación de cadenas.
int cmp_str (const void *a, const void *b) {
	return strcmp ( (char*) a , (char*) b );
}

void prueba_crear_heap_vacio () {
	printf("\nPRUEBA CREAR HEAP VACIO:\n\n");
	heap_t *h = heap_crear (NULL);
	
	print_test("Prueba heap crear heap vacio", h);
	print_test("Prueba heap la cantidad de elementos es 0", heap_cantidad(h) == 0);
	print_test("Prueba heap esta vacio es true", heap_esta_vacio(h));
	print_test("Prueba heap ver max, es NULL, no existe", !heap_ver_max(h));
	print_test("Prueba heap desencolar, es NULL, no existe", !heap_desencolar(h));

	heap_destruir(h,NULL);
}

void prueba_encolar () {
	printf("\nPRUEBA ENCOLAR:\n\n");
	heap_t* h = heap_crear(cmp_str);

	char *elem[] = {"perro", "gato", "vaca"};

	/* Inserta 1 elementos y luego lo borra */
	print_test("Prueba heap encolar elem[0]", heap_encolar(h, elem[0]));
	print_test("Prueba heap la cantidad de elementos es 1", heap_cantidad(h) == 1);
	print_test("Prueba heap esta vacio es false", !heap_esta_vacio(h));
	print_test("Prueba heap ver max es elem[0]", heap_ver_max(h) == elem[0]);
	print_test("Prueba heap ver max es elem[0]", heap_ver_max(h) == elem[0]);
	print_test("Prueba heap desencolar es elem[0]", heap_desencolar(h) == elem[0]);
	print_test("Prueba heap la cantidad de elementos es 0", heap_cantidad(h) == 0);
	print_test("Prueba heap esta vacio es true", heap_esta_vacio(h));

	/* Inserta otros 2 elementos y no los borra (se destruyen con el heap) */
	print_test("Prueba heap encolar elem[1]", heap_encolar(h, elem[1]));
	print_test("Prueba heap la cantidad de elementos es 1", heap_cantidad(h) == 1);
	print_test("Prueba heap esta vacio es false", !heap_esta_vacio(h));
	print_test("Prueba heap ver max es elem[1]", heap_ver_max(h) == elem[1]);
	print_test("Prueba heap ver max es elem[1]", heap_ver_max(h) == elem[1]);
	
	print_test("Prueba heap encolar elem[2]", heap_encolar(h, elem[2]));
	print_test("Prueba heap la cantidad de elementos es 2", heap_cantidad(h) == 2);
	print_test("Prueba heap esta vacio es false", !heap_esta_vacio(h));
	print_test("Prueba heap ver max es elem[2]", heap_ver_max(h) == elem[2]);
	print_test("Prueba heap ver max es elem[2]", heap_ver_max(h) == elem[2]);
	
	heap_destruir(h,NULL);
}

void prueba_desencolar () {
	printf("\nPRUEBA DESENCOLAR:\n\n");
	heap_t* h = heap_crear(cmp_str);
	
	char *elem[] = {"perro", "gato", "vaca", "jirafa", "leon", "raton"};

	/* Inserta 6 elementos y luego los borra */
	print_test("Prueba heap encolar elem[0]", heap_encolar(h, elem[0]));
	print_test("Prueba heap encolar elem[1]", heap_encolar(h, elem[1]));
	print_test("Prueba heap encolar elem[2]", heap_encolar(h, elem[2]));
	print_test("Prueba heap encolar elem[3]", heap_encolar(h, elem[3]));
	print_test("Prueba heap encolar elem[4]", heap_encolar(h, elem[4]));
	print_test("Prueba heap encolar elem[5]", heap_encolar(h, elem[5]));
	
	/*Al borrar cada elemento comprueba el máximo y demás. */
	print_test("Prueba heap ver max es elem[2]", heap_ver_max(h) == elem[2]);
	print_test("Prueba heap desencolar es elem[2]", heap_desencolar(h) == elem[2]);
	print_test("Prueba heap la cantidad de elementos es 5", heap_cantidad(h) == 5);
	
	print_test("Prueba heap ver max es elem[5]", heap_ver_max(h) == elem[5]);
	print_test("Prueba heap desencolar es elem[5]", heap_desencolar(h) == elem[5]);
	print_test("Prueba heap la cantidad de elementos es 4", heap_cantidad(h) == 4);
	
	print_test("Prueba heap ver max es elem[0]", heap_ver_max(h) == elem[0]);
	print_test("Prueba heap desencolar es elem[0]", heap_desencolar(h) == elem[0]);
	print_test("Prueba heap la cantidad de elementos es 3", heap_cantidad(h) == 3);
	
	print_test("Prueba heap ver max es elem[4]", heap_ver_max(h) == elem[4]);
	print_test("Prueba heap desencolar es elem[4]", heap_desencolar(h) == elem[4]);
	print_test("Prueba heap la cantidad de elementos es 2", heap_cantidad(h) == 2);
	
	print_test("Prueba heap ver max es elem[3]", heap_ver_max(h) == elem[3]);
	print_test("Prueba heap desencolar es elem[3]", heap_desencolar(h) == elem[3]);
	print_test("Prueba heap la cantidad de elementos es 1", heap_cantidad(h) == 1);
	
	print_test("Prueba heap ver max es elem[1]", heap_ver_max(h) == elem[1]);
	print_test("Prueba heap desencolar es elem[1]", heap_desencolar(h) == elem[1]);
	print_test("Prueba heap la cantidad de elementos es 0", heap_cantidad(h) == 0);

	heap_destruir(h,NULL);
}

// Función auxiliar de las pruebas de volumen.
bool datos_crear (int* elem[], size_t largo) {
	for (size_t i = 0; i < largo; i++) {
		elem[i] = malloc(sizeof(int));
		if (!elem[i]) return false;
		*(elem[i]) = i+1;
	}
	return true;
}

// Función auxiliar de las pruebas de volumen.
void swap_int (int **int1, int **int2) {
	int* aux = *int1;
	*int1 = *int2;
	*int2 = aux;
}

// Función auxiliar de las pruebas de volumen.
int cmp_int (const void *a, const void *b) {
	return ( *((int*)a) - *((int*)b) );
}

void prueba_volumen (size_t largo, bool debug) {
	printf("\nPRUEBA VOLUMEN CON %zu ELEMENTOS:\n\n",largo);
	
	heap_t* h = heap_crear(cmp_int);
	
	int** elem = calloc (largo,sizeof(int*));
	if (!elem) { heap_destruir(h,NULL); return; }
	
	if (!datos_crear(elem,largo)) {
		free(elem); heap_destruir(h,NULL); return;
	}
	
	/* Inserta y comprueba 'largo' elementos en el heap */
	bool ok_enc  = true;
	bool ok_vmax = true;
	for (int i = 0; i < largo; i++) {	
		ok_enc = heap_encolar(h,elem[i]);
		ok_vmax = (heap_ver_max(h) == elem[i]);
		if ((!ok_enc) || (!ok_vmax)) break;
	}

	if (debug) print_test("Prueba heap encolar muchos elementos", ok_enc);
	if (debug) print_test("Prueba heap ver max de muchos elementos", ok_vmax);
	if (debug) print_test("Prueba heap la cantidad de elementos es correcta", heap_cantidad(h) == largo);
	
	/* Verifica que borre todos los elementos */
	bool ok = true;
	for (size_t i = 0; i < largo; i++) {
			ok = (heap_desencolar(h) != NULL);
			if (!ok) break;
	}

	if (debug) print_test("Prueba heap desencolar muchos elementos", ok);
	if (debug) print_test("Prueba heap la cantidad de elementos es 0", heap_cantidad(h) == 0);
	if (debug) print_test("Prueba heap esta vacio es true", heap_esta_vacio(h));


	/* Inserta 'largo' elementos en el heap */
	ok_enc  = true; ok_vmax = true;
	for (int i = 0; i < largo; i++) {	
		ok_enc = heap_encolar(h,elem[i]);
		ok_vmax = (heap_ver_max(h) == elem[i]);
		if ((!ok_enc) || (!ok_vmax)) break;
	}

	/* Destruye el heap - debería liberar los enteros */
	heap_destruir(h,free);
	free(elem);
}

void prueba_heapsort () {
	printf("\nPRUEBA HEAPSORT:\n\n");
	
	char *elem[] = {"perro","gato","vaca","jirafa","leon","raton","sapo","vinchuca","cocodrilo","rinoceronte"};
	char *elem_ord[] = {"cocodrilo","gato","jirafa","leon","perro","raton","rinoceronte","sapo","vaca","vinchuca"};
	
	heapsort((void*)elem,10,cmp_str);
	
	bool ok = true;
	for (size_t i = 0; i < 10; i++) {
		if (elem[i] != elem_ord[i]) ok = false;
		if (!ok) break;
	}
	
	print_test("Prueba heapsort de un arreglo de 10 elementos", ok);
}
	
/* ******************************************************************
 *                        PROGRAMA PRINCIPAL						*
 * *****************************************************************/

int main (int argc, char** argv) {
	printf ("\nCOMIENZO DE PRUEBAS DE HEAP.\n");
	
	if (argc < 2) {
		prueba_crear_heap_vacio ();
		prueba_encolar ();
		prueba_desencolar ();
		prueba_volumen (5000, true);
		prueba_heapsort ();
	} else {
	    size_t largo = atoi(argv[1]);
    	prueba_volumen (largo, true);
	}
	
	printf ("\nFIN DE PRUEBAS DE HEAP.\n\n");
	return 0;
}
